﻿using System.Collections.Generic;
using Koopakiller.Numerics.Resources;

namespace System.Linq
{
    /// <summary>
    /// Contains extensions for the <see cref="System.Linq"/> namespace.
    /// </summary>
    public static class Extensions_Ling
    {
        /// <summary>
        /// Determines whether a sequence contains a count of elements.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
        /// <param name="source">The <see cref="IEnumerable{T}"/> to check of elements.</param>
        /// <param name="count">The minimum count of elements to return <c>true</c>.</param>
        /// <returns><c>true</c> if the <paramref name="source"/> contains minimum <paramref name="count"/> elements; otherwise, <c>false</c>.</returns>
        public static bool HasCount<TSource>(this IEnumerable<TSource> source, int count)
        {
            if (source == null)
                throw ExceptionHelper.GetArgumentNull("source", "extension", null);
            if (count < 0)
                throw ExceptionHelper.GetArgumentOutOfRange("count", "minval", "0");
            if (count == 0)
                return true;

            using (var enumerator = source.GetEnumerator())
            {
                while (count > 0 && enumerator.MoveNext())
                    --count;
            }
            if (count == 0)
                return true;
            return false;
        }

        /// <summary>
        /// Determines whether a sequence contains a count of elements that satisfies a condition.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
        /// <param name="source">The <see cref="IEnumerable{T}"/> to check of elements.</param>
        /// <param name="count">The minimum count of elements to return <c>true</c>.</param>
        /// <param name="predicate">A function to test each element for a condition.</param>
        /// <returns><c>true</c> if the <paramref name="source"/> contains minimum <paramref name="count"/> elements that satisfies a condition; otherwise, <c>false</c>.</returns>
        public static bool HasCount<TSource>(this IEnumerable<TSource> source, int count, Func<TSource, bool> predicate)
        {
            if (source == null)
                throw ExceptionHelper.GetArgumentNull("source", "extension");
            if (count < 0)
                throw ExceptionHelper.GetArgumentOutOfRange("count", "minval", "0");
            if (predicate == null)
                throw ExceptionHelper.GetArgumentNull("predicate", "extension");
            if (count == 0)
                return true;

            using (var enumerator = source.GetEnumerator())
            {
                while (count > 0 && enumerator.MoveNext())
                    if (predicate(enumerator.Current))
                        --count;
            }
            if (count == 0)
                return true;
            return false;
        }

        /// <summary>
        /// Determine the bitwise exclusive-OR from the elements like <c>a ^ b ^ c</c> (<c>a Xor b Xor c</c> in Visual Basic).
        /// </summary>
        /// <param name="source">The <see cref="IEnumerable{Int32}"/> to calculate the bitwise exclusive-OR.</param>
        /// <returns>The bitwise exclusive-OR from <paramref name="source"/>.</returns>
        /// <seealso href="http://dotnet-snippets.de/snippet/linq-erweiterung-xor/3840"/>
        public static int Xor(this IEnumerable<int> source)
        {
            if (source == null)
                throw ExceptionHelper.GetArgumentNull("source", "extension");
            using (var enu = source.GetEnumerator())
            {
                enu.MoveNext();
                int result = enu.Current;//? first element

                while (enu.MoveNext())
                    result ^= enu.Current;//? other elements

                return result;
            }
        }

        /// <summary>
        /// Compares the items of this list with the items of another.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
        /// <param name="source">The first list.</param>
        /// <param name="other">The second list.</param>
        /// <param name="equal">A function to compare the equality of two items.</param>
        /// <param name="ignoreOrder">Pass <c>true</c> to ignore the order; otherwise, <c>false</c>.</param>
        /// <returns><c>true</c> if the items of <paramref name="source"/> and <paramref name="other"/> equal; otherwise, <c>false</c>.</returns>
        public static bool AreItemsEqual<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> other, Func<TSource, TSource, bool> equal, bool ignoreOrder)
        {
            using (var enu1 = source.GetEnumerator())
            {
                using (var enu2 = other.GetEnumerator())
                {
                    bool b1 = true, b2 = true;
                    if (ignoreOrder)
                        while ((b1 = enu1.MoveNext()) & (b2 = enu2.MoveNext()))
                        {
                            if (!source.Contains(enu2.Current))
                                return false;
                        }
                    else
                        while ((b1 = enu1.MoveNext()) & (b2 = enu2.MoveNext()))
                        {
                            if (!equal(enu1.Current, enu2.Current))
                                return false;
                        }

                    if (b1 != b2)
                        return false;//one at end, the other not
                    else
                        return true;
                }
            }
        }

    }
}
